package com.evil.application.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.evil.application.enums.form.control.ControlCodeEnum;
import com.evil.application.enums.order.OrderSpecialControlEnum;
import com.evil.application.enums.reporting.ReportingTargetTypeEnum;
import com.evil.application.enums.reporting.SummaryTypeEnum;
import com.evil.application.pojo.dto.form.control.code.ControlDTO;
import com.evil.application.pojo.dto.form.control.code.NumberControlDTO;
import com.evil.application.pojo.dto.form.control.code.config.FilterConfigDTO;
import com.evil.application.pojo.dto.order.OrderPageReqDTO;
import com.evil.application.pojo.dto.order.RelatedDataReqDTO;
import com.evil.application.pojo.dto.order.es.SaveESOrderDTO;
import com.evil.application.pojo.dto.reporting.AggBaseDTO;
import com.evil.application.pojo.dto.reporting.ApplicationReportingDTO;
import com.evil.application.pojo.dto.reporting.ReportingHeaderDTO;
import com.evil.application.pojo.dto.reporting.ReportingTargetDTO;
import com.evil.application.pojo.dto.reporting.data.RefreshAppOrderReqDTO;
import com.evil.application.pojo.dto.reporting.data.list.AccurateQueryReqDTO;
import com.evil.application.pojo.dto.reporting.data.list.ReportingDataListReqDTO;
import com.evil.application.pojo.entity.ApplicationFormControl;
import com.evil.application.pojo.entity.ApplicationInfo;
import com.evil.application.pojo.entity.ApplicationOrder;
import com.evil.application.pojo.entity.ApplicationOrderNodeHandler;
import com.evil.application.service.ApplicationFormControlService;
import com.evil.application.service.ElasticsearchService;
import com.evil.application.util.ApplicationUtil;
import com.evil.application.util.OrderDataUtil;
import com.evil.common.application.dto.judge.JudgeOr;
import com.evil.common.application.dto.order.OrderPageDataRespDTO;
import com.evil.common.application.dto.order.OrderQueryReqDTO;
import com.evil.common.application.dto.order.data.ControlDataDTO;
import com.evil.common.application.dto.order.data.ControlDataValueDTO;
import com.evil.common.application.enums.QueryTypeEnum;
import com.evil.common.application.enums.judge.JudgeSignEnum;
import com.evil.common.application.enums.order.OrderProcessStatusEnum;
import com.evil.common.application.enums.order.OrderSearchTypeEnum;
import com.evil.common.core.constant.AppConstant;
import com.evil.common.core.entity.Order;
import com.evil.common.core.entity.QueryModel;
import com.evil.common.core.enums.DateTypeEnum;
import com.evil.common.core.enums.RCodeEnum;
import com.evil.common.core.enums.SwitchEnum;
import com.evil.common.core.exception.BusinessException;
import com.evil.common.core.util.NumberUtils;
import com.evil.common.core.util.StreamUtil;
import com.evil.common.es.dto.Summary;
import com.evil.common.es.dto.application.order.ESOrderDataDTO;
import com.evil.common.es.dto.application.order.data.ESControlDataDTO;
import com.evil.common.es.dto.application.order.data.ESControlDataValueDTO;
import com.evil.common.es.dto.application.order.node.ESNodeHandlerDTO;
import com.evil.common.es.util.ESFieldUtil;
import com.evil.common.es.util.ElasticsearchUtil;
import com.github.pagehelper.PageInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ReverseNestedAggregationBuilder;
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder;
import org.elasticsearch.search.sort.NestedSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.Settings;
import org.springframework.data.elasticsearch.core.query.ByQueryResponse;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 搜索引擎
 *
 * @author liyang
 * @date 2022-08-08 00:00
 */
@Slf4j
@Service
@AllArgsConstructor
public class ElasticsearchServiceImpl implements ElasticsearchService {

    private final ApplicationFormControlService applicationFormControlService;

    private final ElasticsearchRestTemplate elasticsearchRestTemplate;

    // region index

    /**
     * 创建索引
     */
    @Override
    public void createIndex() {
        IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(ESOrderDataDTO.class);
        indexOperations.delete();
        Settings settings = indexOperations.createSettings();
        // 返回最大行数
        settings.put("index.max_result_window", "65536");

        Document mapping = indexOperations.createMapping();
        // 关闭 日期自动检测
        mapping.put("date_detection", false);
//        mapping.put("total_fields.limit", 3000);

        indexOperations.create(settings, mapping);
        indexOperations.refresh();
    }

    /**
     * 删除索引
     */
    @Override
    public void deleteIndex() {
        IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(ESOrderDataDTO.class);
        indexOperations.delete();
    }

    // endregion

    // region select

    /**
     * ES：是否存在该控件值订单
     *
     * @param controlDataDTO controlDataDTO
     * @return boolean
     */
    @Override
    public boolean isExistOrder(ControlDataDTO controlDataDTO) {
        // 创建bool多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 表格
        if (ControlCodeEnum.TABLE.getCode().equals(controlDataDTO.getControlCode())) {
            // 创建bool多条件查询
            BoolQueryBuilder rowsQueryBuilder = QueryBuilders.boolQuery();
            controlDataDTO.getTableValues().forEach(row -> {
                // 创建bool多条件查询
                BoolQueryBuilder columnsQueryBuilder = QueryBuilders.boolQuery();
                row.forEach(column -> this.queryByControlData(columnsQueryBuilder, column));
                rowsQueryBuilder.should(columnsQueryBuilder);
            });
            boolQueryBuilder.must(rowsQueryBuilder);
        }
        // 非表格
        else {
            this.queryByControlData(boolQueryBuilder, controlDataDTO);
        }

        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        searchQueryBuilder.withQuery(boolQueryBuilder);
        searchQueryBuilder.withTrackTotalHits(true);
        return !this.orderDataList(searchQueryBuilder.build()).isEmpty();
    }

    /**
     * 订单数据
     *
     * @param orderId orderId
     * @return OrderPageDataRespDTO
     */
    @Override
    public ESOrderDataDTO esOrder(Long orderId) {
        String id = Long.toString(orderId);
        ESOrderDataDTO esOrderDataDTO = elasticsearchRestTemplate.get(id, ESOrderDataDTO.class);
        if (Objects.isNull(esOrderDataDTO)) {
            throw new BusinessException(RCodeEnum.APPLICATION_ORDER_NOT_EXIST);
        }
        return esOrderDataDTO;
    }

    /**
     * 订单数据
     *
     * @param orderId orderId
     * @return OrderPageDataRespDTO
     */
    @Override
    public OrderPageDataRespDTO order(Long orderId) {
        return this.esOrder(orderId).toOrderPageDataRespDTO();
    }

    /**
     * 订单数据
     *
     * @param orderId orderId
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ControlDataDTO> orderDataList(Long orderId) {
        return this.order(orderId).getData();
    }

    /**
     * 订单数据列表
     *
     * @param orderIds orderIds
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ESOrderDataDTO> esOrders(Long... orderIds) {
        return this.esOrders(Arrays.asList(orderIds));
    }

    /**
     * 订单数据列表
     *
     * @param orderIds orderIds
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ESOrderDataDTO> esOrders(Collection<Long> orderIds) {
        String[] ids = orderIds.stream().map(e -> Long.toString(e)).toArray(String[]::new);
        if (ids.length <= 0) {
            return new ArrayList<>();
        }
        // 创建bool多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.idsQuery().addIds(ids));
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        searchQueryBuilder.withQuery(boolQueryBuilder);
        searchQueryBuilder.withTrackTotalHits(true);
        SearchHits<ESOrderDataDTO> searchHits = elasticsearchRestTemplate.search(searchQueryBuilder.build(), ESOrderDataDTO.class);
        return searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    /**
     * 订单数据列表
     *
     * @param applicationKey 应用key
     * @param orderSns       订单编号
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ESOrderDataDTO> esOrders(String applicationKey, Collection<String> orderSns) {
        return this.esOrders(applicationKey, orderSns.toArray(new String[]{}));
    }

    /**
     * 订单数据列表
     *
     * @param applicationKey 应用key
     * @param orderSns       订单编号
     * @return OrderPageDataRespDTO
     */
    @Override
    public List<ESOrderDataDTO> esOrders(String applicationKey, String... orderSns) {
        if (orderSns.length <= 0) {
            return new ArrayList<>();
        }
        // 创建bool多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.termQuery(ESOrderDataDTO.COL_AK, applicationKey));
        boolQueryBuilder.must(QueryBuilders.termsQuery(ESOrderDataDTO.COL_OS, orderSns));
        NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder();
        searchQueryBuilder.withQuery(boolQueryBuilder);
        searchQueryBuilder.withTrackTotalHits(true);
        SearchHits<ESOrderDataDTO> searchHits = elasticsearchRestTemplate.search(searchQueryBuilder.build(), ESOrderDataDTO.class);
        return searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    /**
     * 订单ids
     *
     * @param orderPageReqDTO orderPageReqDTO
     * @return List
     */
    @Override
    public List<Long> orderIds(OrderPageReqDTO orderPageReqDTO) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(this.createBoolQueryBuilder(orderPageReqDTO));
        queryBuilder.withFields(ESOrderDataDTO.COL_ID);
        queryBuilder.withTrackTotalHits(true);

        SearchHits<ESOrderDataDTO> searchHits = elasticsearchRestTemplate.search(queryBuilder.build(), ESOrderDataDTO.class);
        return searchHits.stream().map(e -> e.getContent().getOrderId()).collect(Collectors.toList());
    }

    /**
     * 订单数据列表
     *
     * @param orderPageReqDTO orderPageReqDTO
     * @return List
     */
    @Override
    public List<OrderPageDataRespDTO> orders(OrderPageReqDTO orderPageReqDTO) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(this.createBoolQueryBuilder(orderPageReqDTO));
        queryBuilder.withTrackTotalHits(true);

        return this.orderDataList(queryBuilder.build());
    }

    /**
     * 关联订单数据列表
     *
     * @param queryModel queryModel
     * @return List
     */
    @Override
    public List<ControlDataDTO> relatedDataList(QueryModel<RelatedDataReqDTO> queryModel) {
        RelatedDataReqDTO param = queryModel.getParam();
        ControlDTO control = param.getControl();

        String title = "related";
        // 构造query
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withTrackTotalHits(true);

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 应用id
        boolQueryBuilder.must(QueryBuilders.matchQuery(ESOrderDataDTO.COL_AI, param.getApplicationId()));
        // 关键字
        if (StringUtils.isNotBlank(param.getKeyword())) {
            BoolQueryBuilder keywordQuery = QueryBuilders.boolQuery();
            // 关键字-控件
            keywordQuery.must(QueryBuilders.termQuery(ESControlDataDTO.COL_DN_CI, control.getControlId()));
            // 关键字-值
            QueryBuilder keywordValueQuery = this.handleKeyword(param.getKeyword(), param.getFuzzyQuery(), ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD);
            // 关键字-值-nested
            this.reversalQuery(false, keywordQuery, keywordValueQuery);
            // 关键字-控件-nested
            boolQueryBuilder.must(QueryBuilders.nestedQuery(ESOrderDataDTO.COL_DN, keywordQuery, ScoreMode.None));
        }
        // 筛选条件
        this.handleJudgeOrs(boolQueryBuilder, param.getJudgeOrs(), param.getForceUseValue());
        queryBuilder.withQuery(boolQueryBuilder);

        // 汇总查询
        if (Objects.nonNull(param.getSummaryType())) {
            AggregationDTO aggregationDTO = new AggregationDTO(null, null);
            aggregationDTO.beingTableControl = param.getBeingTableControl();
            ReportingTargetDTO target = new ReportingTargetDTO(title, control.getControlId(), control.getControlTitle(), param.getSummaryType());
            this.handleAggregation(aggregationDTO, true, target);
            queryBuilder.addAggregation(aggregationDTO.root);

            ControlDataDTO controlDataDTO = ApplicationUtil.controlToControlData(control);

            SearchHits<ESOrderDataDTO> search = elasticsearchRestTemplate.search(queryBuilder.build(), ESOrderDataDTO.class);
            if (Objects.nonNull(search.getAggregations())) {
                Map<Summary<String>, Aggregations> aggsMap = ElasticsearchUtil.aggregationsData(new RelatedSummary(title, null), search.getAggregations());
                controlDataDTO.setValues(aggsMap.keySet().stream().map(e -> {
                    String value = e.getValue();
                    // 需要处理精度
                    if (control instanceof NumberControlDTO && Objects.nonNull(((NumberControlDTO) control).getDecimalAccuracy())) {
                        BigDecimal bigDecimal = NumberUtils.toBigDecimal(e.getValue(), null);
                        value = bigDecimal.setScale(((NumberControlDTO) control).getDecimalAccuracy(), RoundingMode.HALF_UP).toPlainString();
                    }
                    return new ControlDataValueDTO(value);
                }).collect(Collectors.toList()));
            }
            return ListUtil.toList(controlDataDTO);
        }
        // 普通查询
        else {
            queryBuilder.withPageable(PageRequest.of(queryModel.getPageNum() - 1, queryModel.getPageSize()));
            SearchHits<ESOrderDataDTO> search = elasticsearchRestTemplate.search(queryBuilder.build(), ESOrderDataDTO.class);

            List<ControlDataDTO> controlDataList = new ArrayList<>();
            search.forEach(e -> controlDataList.addAll(e.getContent().getDataNested().stream()
                    .filter(c -> c.getControlId().equals(param.getControl().getControlId()))
                    .map(ESControlDataDTO::toControlDataDTO)
                    .collect(Collectors.toList())));
            // 过滤重复值的控件
            return OrderDataUtil.filterRepeat(controlDataList);
        }
    }

    /**
     * 订单数据分页
     *
     * @param queryModel queryModel
     * @return PageInfo
     */
    @Override
    public PageInfo<OrderPageDataRespDTO> orderDataPage(QueryModel<OrderPageReqDTO> queryModel) {
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(this.createBoolQueryBuilder(queryModel.getParam()));
        queryBuilder.withPageable(PageRequest.of(queryModel.getPageNum() - 1, queryModel.getPageSize()));
        queryBuilder.withTrackTotalHits(true);
        // 处理排序
        this.handleSort(queryBuilder, queryModel);

        return this.orderDataPage(queryBuilder.build());
    }

    /**
     * 报表订单数据分页
     *
     * @param reportingDataListReqDTO reportingDataListReqDTO
     * @return PageInfo
     */
    @Override
    public PageInfo<OrderPageDataRespDTO> orderDataPage(ReportingDataListReqDTO reportingDataListReqDTO) {
        QueryModel<OrderPageReqDTO> queryModel = reportingDataListReqDTO.getQueryModel();

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(this.createBoolQueryBuilder(reportingDataListReqDTO));
        queryBuilder.withPageable(PageRequest.of(queryModel.getPageNum() - 1, queryModel.getPageSize()));
        queryBuilder.withTrackTotalHits(true);
        // 处理排序
        this.handleSort(queryBuilder, queryModel);

        return this.orderDataPage(queryBuilder.build());
    }

    /**
     * 报表订单数据 汇总
     *
     * @param reportingDTO reportingDTO
     */
    @Override
    public Aggregations orderDataSummary(ApplicationReportingDTO reportingDTO) {
        // 创建bool多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 应用id
        boolQueryBuilder.must(QueryBuilders.termQuery(ESOrderDataDTO.COL_AI, reportingDTO.getApplicationId()));
        // 报表汇总过滤草稿
        boolQueryBuilder.mustNot(QueryBuilders.termQuery(ESOrderDataDTO.COL_PS, OrderProcessStatusEnum.DRAFT_BOX.getId()));
        // 筛选条件
        if (SwitchEnum.isOpen(reportingDTO.getFilterStatus())) {
            FilterConfigDTO filterConfig = reportingDTO.getFilterConfig();
            this.handleJudgeOrs(boolQueryBuilder, filterConfig.getJudgeOrs(), false);
        }

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withMaxResults(0);
        this.createAggregationBuilder(reportingDTO).forEach(queryBuilder::withAggregations);

        SearchHits<ESOrderDataDTO> search = elasticsearchRestTemplate.search(queryBuilder.build(), ESOrderDataDTO.class);
        return ElasticsearchUtil.getAggregations(search.getAggregations());
    }

    // endregion

    // region save

    /**
     * 保存订单数据
     *
     * @param esOrderDataDTO esOrderDataDTO
     */
    @Override
    public void save(ESOrderDataDTO esOrderDataDTO) {
        elasticsearchRestTemplate.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
        try {
            elasticsearchRestTemplate.save(esOrderDataDTO);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(RCodeEnum.ELASTICSEARCH_ERROR);
        }
    }

    /**
     * 保存订单
     *
     * @param order           applicationOrder
     * @param app             applicationInfo
     * @param controlDataList controlDataList
     */
    @Override
    public void save(ApplicationInfo app, ApplicationOrder order, List<ControlDataDTO> controlDataList) {
        ESOrderDataDTO esOrderDataDTO = this.initESOrder(app, order, controlDataList, new ArrayList<>());
        this.save(esOrderDataDTO);
    }

    /**
     * 局部更新订单数据（不支持更新表格里面的局部数据，允许整个表格更新）
     *
     * @param order          order
     * @param controlDataMap controlDataMap
     */
    @Override
    public void save(ApplicationOrder order, Map<Long, ControlDataDTO> controlDataMap) {
        ESOrderDataDTO esOrder = this.esOrder(order.getOrderId());
        esOrder.setUpdated(order.getUpdated());
        esOrder.setModifierId(order.getModifierId());
        this.save(esOrder, controlDataMap);
    }

    /**
     * 局部更新订单数据（不支持更新表格里面的局部数据，允许整个表格更新）
     *
     * @param esOrder           esOrder
     * @param controlDataDTOMap controlDataDTOMap
     */
    @Override
    public void save(ESOrderDataDTO esOrder, Map<Long, ControlDataDTO> controlDataDTOMap) {
        Map<Long, ESControlDataDTO> esControlDataMap = StreamUtil.toOrderlyMapK(esOrder.getData(), ESControlDataDTO::getControlId);
        BiFunction<String, ControlDataValueDTO, String> getDataValue = (code, value) -> ControlCodeEnum.findByCode(code).getDataValueToString().apply(value);
        controlDataDTOMap.forEach((controlId, data) -> {
            ESControlDataDTO esControlDataDTO = new ESControlDataDTO(data, getDataValue);
            if (esControlDataMap.containsKey(controlId)) {
                esControlDataMap.replace(controlId, esControlDataDTO);
            } else {
                esControlDataMap.put(controlId, esControlDataDTO);
            }
        });
        esOrder.setData(new ArrayList<>(esControlDataMap.values()), code -> ControlCodeEnum.TABLE.getCode().equals(code));

        this.save(esOrder);
    }

    /**
     * 保存订单数据
     *
     * @param esOrders esOrders
     */
    @Override
    public void saveBatch(Collection<ESOrderDataDTO> esOrders) {
        elasticsearchRestTemplate.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
        try {
            CollectionUtil.split(esOrders, 100).forEach(elasticsearchRestTemplate::save);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(RCodeEnum.ELASTICSEARCH_ERROR);
        }
    }

    /**
     * 保存订单数据
     *
     * @param saveESOrders saveESOrders
     */
    @Override
    public void saveBatch(List<SaveESOrderDTO> saveESOrders) {
        List<ESOrderDataDTO> osOrders = StreamUtil.transListT(saveESOrders, e -> this.initESOrder(e.getApp(), e.getOrder(), e.getData(), e.getNodeHandlers()));
        this.saveBatch(osOrders);
    }

    /**
     * 局部更新订单数据（不支持更新表格里面的局部数据，允许整个表格更新）
     *
     * @param orderMap      orderMap
     * @param ordersDataMap ordersDataMap
     */
    @Override
    public void refreshBatch(Map<Long, ApplicationOrder> orderMap, Map<Long, List<ControlDataDTO>> ordersDataMap) {
        List<ESOrderDataDTO> esOrderDataDTOS = this.esOrders(orderMap.keySet());
        Map<Long, ESOrderDataDTO> esOrderMap = StreamUtil.toMapK(esOrderDataDTOS, ESOrderDataDTO::getOrderId);
        ordersDataMap.forEach((orderId, data) -> {
            if (!orderMap.containsKey(orderId) || !esOrderMap.containsKey(orderId)) {
                throw new BusinessException(RCodeEnum.ERROR_RUNTIME);
            }
            ApplicationOrder order = orderMap.get(orderId);
            ESOrderDataDTO esOrder = esOrderMap.get(orderId);
            esOrder.setUpdated(order.getUpdated());
            esOrder.setModifierId(order.getModifierId());
            this.handleESOrderData(esOrder, data);
        });

        this.saveBatch(esOrderDataDTOS);
    }

    /**
     * 局部更新订单数据（不支持更新表格里面的局部数据，允许整个表格更新）
     *
     * @param orderDataMap orderDataMap
     */
    @Override
    public void refreshBatch(Map<ApplicationOrder, List<ControlDataDTO>> orderDataMap) {
        List<ESOrderDataDTO> esOrders = this.esOrders(StreamUtil.transListT(orderDataMap.keySet(), ApplicationOrder::getOrderId));
        Map<Long, ESOrderDataDTO> esOrderMap = StreamUtil.toMapK(esOrders, ESOrderDataDTO::getOrderId);

        orderDataMap.forEach((order, data) -> {
            if (!esOrderMap.containsKey(order.getOrderId())) {
                throw new BusinessException(RCodeEnum.ERROR_RUNTIME);
            }
            ESOrderDataDTO esOrder = esOrderMap.get(order.getOrderId());
            esOrder.setUpdated(order.getUpdated());
            esOrder.setModifierId(order.getModifierId());
            this.handleESOrderData(esOrder, data);
        });

        this.saveBatch(esOrders);
    }
    // endregion

    // region del

    /**
     * 删除订单数据
     *
     * @param refreshAppOrderReqDTO refreshAppOrderReqDTO
     */
    @Override
    public void delete(RefreshAppOrderReqDTO refreshAppOrderReqDTO) {
        log.info("【删除订单数据】，参数：{}", JSON.toJSONString(refreshAppOrderReqDTO));
        elasticsearchRestTemplate.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
        ByQueryResponse queryResponse = elasticsearchRestTemplate.delete(this.handle(refreshAppOrderReqDTO), ESOrderDataDTO.class);
        log.info("【删除订单数据】，参数：{}，结果：{}", JSON.toJSONString(refreshAppOrderReqDTO), JSON.toJSON(queryResponse));
    }

    /**
     * 删除订单数据
     *
     * @param orderIds orderId
     */
    @Override
    public void delete(Long... orderIds) {
        List<String> deleteIds = Arrays.stream(orderIds).map(e -> Long.toString(e)).collect(Collectors.toList());
        CollectionUtil.split(deleteIds, 50).forEach(list -> this.delete(list.toArray(new String[]{})));
    }

    /**
     * 删除订单数据
     *
     * @param orderIds orderIds
     */
    @Override
    public void delete(Collection<Long> orderIds) {
        this.delete(orderIds.stream().map(id -> Long.toString(id)).toArray(String[]::new));
    }

    /**
     * 删除订单数据
     *
     * @param ids ids
     */
    @Override
    public void delete(String... ids) {
        if (ids.length < 1) {
            return;
        }
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder().withQuery(QueryBuilders.idsQuery().addIds(ids));
        try {
            log.info("【删除ES订单数据】，订单：{}", JSON.toJSONString(ids));
            elasticsearchRestTemplate.setRefreshPolicy(RefreshPolicy.IMMEDIATE);
            ByQueryResponse response = elasticsearchRestTemplate.delete(queryBuilder.build(), ESOrderDataDTO.class);
            log.info("【删除ES订单数据】，订单：{}，结果：{}", ids, JSON.toJSONString(response));
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(RCodeEnum.ELASTICSEARCH_ERROR);
        }
    }
    // endregion

    /**
     * 初始化搜索引擎订单数据
     *
     * @param order           applicationOrder
     * @param app             applicationInfo
     * @param controlDataList controlDataList
     * @param nodeHandlers    nodeHandlers
     * @return ESOrderDataDTO
     */
    @Override
    public ESOrderDataDTO initESOrder(ApplicationInfo app, ApplicationOrder order, List<ControlDataDTO> controlDataList, List<ESNodeHandlerDTO> nodeHandlers) {
        ESOrderDataDTO esOrder = new ESOrderDataDTO();
        esOrder.setApplicationKey(app.getApplicationKey());
        esOrder.setApplicationId(order.getApplicationId());
        esOrder.setEnterpriseId(order.getEnterpriseId());
        esOrder.setOrderId(order.getOrderId());
        esOrder.setOrderSn(order.getOrderSn());
        esOrder.setProcessStatus(order.getProcessStatus());
        esOrder.setCreatorId(order.getCreatorId());
        esOrder.setModifierId(order.getModifierId());
        esOrder.setCreated(order.getCreated());
        esOrder.setUpdated(order.getUpdated());

        this.handleESOrderData(esOrder, controlDataList);
        esOrder.setNodeHandlers(nodeHandlers);

        return esOrder;
    }

    /**
     * 构建ES节点处理人实体
     *
     * @param nodeHandlers nodeHandlers
     * @return ESOrderDataDTO
     */
    @Override
    public List<ESNodeHandlerDTO> initESNodeHandler(List<ApplicationOrderNodeHandler> nodeHandlers) {
        return StreamUtil.transListT(nodeHandlers, nh -> BeanUtil.copyProperties(nh, ESNodeHandlerDTO.class));
    }

    /**
     * 处理ES 订单数据
     *
     * @param esOrderDataDTO  esOrderDataDTO
     * @param controlDataList controlDataList
     */
    private void handleESOrderData(ESOrderDataDTO esOrderDataDTO, List<ControlDataDTO> controlDataList) {
        BiFunction<String, ControlDataValueDTO, String> getDataValue = (code, value) -> ControlCodeEnum.findByCode(code).getDataValueToString().apply(value);
        esOrderDataDTO.setData(ESControlDataDTO.toESControlDataDTO(controlDataList, getDataValue), code -> ControlCodeEnum.TABLE.getCode().equals(code));
    }

    /**
     * 列表查询
     *
     * @param query query
     * @return List
     */
    private List<OrderPageDataRespDTO> orderDataList(Query query) {
        SearchHits<ESOrderDataDTO> searchHits = elasticsearchRestTemplate.search(query, ESOrderDataDTO.class);
        return searchHits.stream().map(e -> e.getContent().toOrderPageDataRespDTO()).collect(Collectors.toList());
    }

    /**
     * 分页查询
     *
     * @param query query
     * @return PageInfo
     */
    private PageInfo<OrderPageDataRespDTO> orderDataPage(Query query) {
        SearchHits<ESOrderDataDTO> searchHits = elasticsearchRestTemplate.search(query, ESOrderDataDTO.class);

        SearchPage<ESOrderDataDTO> page = SearchHitSupport.searchPageFor(searchHits, query.getPageable());
        PageInfo<OrderPageDataRespDTO> pageInfo = new PageInfo<>();
        pageInfo.setPageNum(page.getNumber() + 1);
        pageInfo.setPageSize(page.getSize());
        pageInfo.setPages(page.getTotalPages());
        pageInfo.setTotal(page.getTotalElements());
        pageInfo.setList(page.getContent().stream().map(e -> e.getContent().toOrderPageDataRespDTO()).collect(Collectors.toList()));
        pageInfo.calcByNavigatePages(PageInfo.DEFAULT_NAVIGATE_PAGES);
        return pageInfo;
    }

    /**
     * 构造查询条件
     *
     * @param orderPageReqDTO orderPageReqDTO
     * @return QueryBuilder
     */
    private QueryBuilder createBoolQueryBuilder(OrderPageReqDTO orderPageReqDTO) {
        // 创建bool多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 企业id
        if (Objects.nonNull(orderPageReqDTO.getEnterpriseId())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(ESOrderDataDTO.COL_EI, orderPageReqDTO.getEnterpriseId()));
        }
        // 应用key
        if (Objects.nonNull(orderPageReqDTO.getApplicationId())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(ESOrderDataDTO.COL_AI, orderPageReqDTO.getApplicationId()));
        }
        // 应用key
        if (StringUtils.isNotBlank(orderPageReqDTO.getApplicationKey())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(ESOrderDataDTO.COL_AK, orderPageReqDTO.getApplicationKey()));
        }
        // 流程状态
        if (Objects.nonNull(orderPageReqDTO.getProcessStatus())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(ESOrderDataDTO.COL_PS, orderPageReqDTO.getProcessStatus()));
        }
        // 流程状态
        if (Objects.nonNull(orderPageReqDTO.getNotEqProcessStatus())) {
            boolQueryBuilder.mustNot(QueryBuilders.matchQuery(ESOrderDataDTO.COL_PS, orderPageReqDTO.getNotEqProcessStatus()));
        }
        // 流程状态
        if (CollectionUtil.isNotEmpty(orderPageReqDTO.getInProcessStatus())) {
            boolQueryBuilder.must(QueryBuilders.termsQuery(ESOrderDataDTO.COL_PS, orderPageReqDTO.getInProcessStatus()));
        }
        // 发起人
        if (Objects.nonNull(orderPageReqDTO.getCreatorId())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(ESOrderDataDTO.COL_CI, orderPageReqDTO.getCreatorId()));
        }
        // 节点处理人 （或 处理）
        if (CollectionUtil.isNotEmpty(orderPageReqDTO.getNodeHandlers())) {
            BoolQueryBuilder nodeHandlersQuery = QueryBuilders.boolQuery();
            orderPageReqDTO.getNodeHandlers().forEach(nodeHandler -> {
                BoolQueryBuilder nodeHandlerQuery = QueryBuilders.boolQuery();
                boolean has = false;
                if (Objects.nonNull(nodeHandler.getNodeId())) {
                    nodeHandlerQuery.must(QueryBuilders.termQuery(ESNodeHandlerDTO.COL_NODE_HANDLERS_NESTED_NODE_ID, nodeHandler.getNodeId()));
                    has = true;
                }
                if (Objects.nonNull(nodeHandler.getHandlerId())) {
                    nodeHandlerQuery.must(QueryBuilders.termQuery(ESNodeHandlerDTO.COL_NODE_HANDLERS_NESTED_HANDLER_ID, nodeHandler.getHandlerId()));
                    has = true;
                }
                if (Objects.nonNull(nodeHandler.getHandleType())) {
                    nodeHandlerQuery.must(QueryBuilders.termQuery(ESNodeHandlerDTO.COL_NODE_HANDLERS_NESTED_HANDLE_TYPE, nodeHandler.getHandleType()));
                    has = true;
                }
                if (has) {
                    nodeHandlersQuery.should(QueryBuilders.nestedQuery(ESOrderDataDTO.COL_NHN, nodeHandlerQuery, ScoreMode.None));
                }
            });
            boolQueryBuilder.must(nodeHandlersQuery);
        }
        // 全局关键字
        if (StringUtils.isNotBlank(orderPageReqDTO.getKeyword())) {
            QueryBuilder keywordQuery = this.handleKeyword(orderPageReqDTO.getKeyword(), orderPageReqDTO.getFuzzyQuery(), ESControlDataValueDTO.COL_DN_V_DV_KEYWORD);
            boolQueryBuilder.must(QueryBuilders.nestedQuery(ESOrderDataDTO.COL_DN, keywordQuery, ScoreMode.None));
        }

        // 筛选参数
        this.handleQuery(boolQueryBuilder, orderPageReqDTO.getQueries());
        // 筛选条件
        this.handleJudgeOrs(boolQueryBuilder, orderPageReqDTO.getJudgeOrs(), orderPageReqDTO.getForceUseValue());
        return boolQueryBuilder;
    }

    /**
     * 构造查询条件
     *
     * @param reportingDataListReqDTO reportingDataListReqDTO
     * @return QueryBuilder
     */
    private QueryBuilder createBoolQueryBuilder(ReportingDataListReqDTO reportingDataListReqDTO) {
        QueryModel<OrderPageReqDTO> queryModel = reportingDataListReqDTO.getQueryModel();
        List<AccurateQueryReqDTO> accurateQueries = reportingDataListReqDTO.getAccurateQueries();

        // 创建bool多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(this.createBoolQueryBuilder(queryModel.getParam()));
        boolQueryBuilder.mustNot(QueryBuilders.termQuery(ESOrderDataDTO.COL_PS, OrderProcessStatusEnum.DRAFT_BOX.getId()));

        boolean fuzzy = QueryTypeEnum.isFuzzy(reportingDataListReqDTO.getQueryType());
        accurateQueries.forEach(accurateQuery -> {
            // 创建bool多条件查询
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

            boolQuery.must(QueryBuilders.matchQuery(ESControlDataDTO.COL_DN_CI, accurateQuery.getControlId()));
            if (fuzzy) {
                boolQuery.must(QueryBuilders.fuzzyQuery(ESControlDataValueDTO.COL_DN_VN_VALUE_KEYWORD, accurateQuery.getSearchKey()));
            } else {
                boolQuery.must(QueryBuilders.matchQuery(ESControlDataValueDTO.COL_DN_VN_VALUE_KEYWORD, accurateQuery.getSearchKey()));
            }
            boolQuery.must(boolQuery);
        });

        return boolQueryBuilder;
    }

    /**
     * 构造报表汇总条件
     *
     * @param reportingDTO reportingDTO
     * @return AbstractAggregationBuilder
     */
    private List<AbstractAggregationBuilder<?>> createAggregationBuilder(ApplicationReportingDTO reportingDTO) {
        AggregationDTO aggregationDTO = new AggregationDTO(null, null);

        this.handleRow(aggregationDTO, reportingDTO.getReportingHeaders());

        this.handleCell(aggregationDTO, reportingDTO.getReportingColumnHeaders());

        this.handleTarget(aggregationDTO, reportingDTO.getReportingTargets());

        if (CollectionUtil.isEmpty(aggregationDTO.targets)) {
            aggregationDTO.targets.add(aggregationDTO.root);
        }
        return aggregationDTO.targets;
    }

    /**
     * 根据控件值查询
     *
     * @param queryBuilder   queryBuilder
     * @param controlDataDTO controlDataDTO
     */
    private void queryByControlData(BoolQueryBuilder queryBuilder, ControlDataDTO controlDataDTO) {
        // 创建bool多条件查询
        BoolQueryBuilder valueQueryBuilder = QueryBuilders.boolQuery();
        ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(controlDataDTO.getControlCode());
        // 表单控件id
        valueQueryBuilder.must(QueryBuilders.termQuery(ESControlDataDTO.COL_DN_CI, controlDataDTO.getControlId()));
        // 数据id
        if (Objects.nonNull(controlDataDTO.getOrderDataId())) {
            valueQueryBuilder.mustNot(QueryBuilders.termQuery(ESControlDataDTO.COL_DN_ODI, controlDataDTO.getOrderDataId()));
        }

        String key = controlCodeEnum.valueUseId() ? ESControlDataValueDTO.COL_DN_VN_ID : ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD;
        List<String> values = OrderDataUtil.getControlValues(controlCodeEnum, controlDataDTO.getValues());

        this.reversalQuery(false, valueQueryBuilder, QueryBuilders.termsQuery(key, values));
        queryBuilder.must(QueryBuilders.nestedQuery(ESOrderDataDTO.COL_DN, valueQueryBuilder, ScoreMode.None));
    }

    /**
     * 处理查询条件
     *
     * @param boolQueryBuilder boolQueryBuilder
     * @param queries          queries
     */
    private void handleQuery(BoolQueryBuilder boolQueryBuilder, List<OrderQueryReqDTO> queries) {
        // 补充控件code
        List<Long> controlIds = queries.stream().map(OrderQueryReqDTO::getControlId).collect(Collectors.toList());
        Map<Long, String> controlCodeMap = applicationFormControlService.controlCodeMap(controlIds);
        queries.forEach(q -> q.setControlCode(controlCodeMap.getOrDefault(q.getControlId(), q.getControlCode())));

        queries.forEach(query -> {
            BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();

            queryBuilder.must(QueryBuilders.termQuery(ESControlDataDTO.COL_DN_CI, query.getControlId()));

            // 是否反转
            boolean reversal = StringUtils.isNotBlank(query.getSearchType()) && OrderSearchTypeEnum.BLANK.getType().equals(query.getSearchType());
            // 下拉项查询
            if (CollectionUtil.isNotEmpty(query.getSearchOptions())) {
                TermsQueryBuilder optionQueryBuilder = QueryBuilders.termsQuery(ESControlDataValueDTO.COL_DN_VN_ID, query.getSearchOptions());
                // 是否反转查询
                this.reversalQuery(reversal, queryBuilder, optionQueryBuilder);
            }
            // 范围查询
            else if (SwitchEnum.isOpen(query.getScope())) {
                // 有任意一个最小最大值
                if (StringUtils.isNotBlank(query.getMinValue()) || StringUtils.isNotBlank(query.getMinValue())) {
                    ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(query.getControlCode());
                    boolean isDate = controlCodeEnum.isDate();
                    String field = ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD;
                    if (isDate) {
                        field = ESControlDataValueDTO.COL_DN_VN_DATE;
                    } else if (controlCodeEnum == ControlCodeEnum.RELATED_SELECT) {
                        ApplicationFormControl control = applicationFormControlService.findById(query.getControlId());
                        if (SwitchEnum.isOpen(control.getSummaryStatus())) {
                            field = ESControlDataValueDTO.COL_DN_VN_DATA;
                        }
                    }
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(field);
                    if (StringUtils.isNotBlank(query.getMaxValue())) {
                        // 开闭区间
                        this.handleDate(isDate, query.getMaxValue(), SwitchEnum.isOpen(query.getMaxType()) ? rangeQueryBuilder::lte : rangeQueryBuilder::lt);
                    }
                    if (StringUtils.isNotBlank(query.getMinValue())) {
                        // 开闭区间
                        this.handleDate(isDate, query.getMinValue(), SwitchEnum.isOpen(query.getMaxType()) ? rangeQueryBuilder::gte : rangeQueryBuilder::gt);
                    }
                    // 是否反转查询
                    this.reversalQuery(reversal, queryBuilder, rangeQueryBuilder);
                }
            }
            // 关键字查询
            else if (StringUtils.isNotBlank(query.getKeyword())) {
                QueryBuilder keywordQuery = this.handleKeyword(query.getKeyword(), query.getFuzzyQuery(), ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD);
                // 是否反转查询
                this.reversalQuery(reversal, queryBuilder, keywordQuery);
            }
            // 是否填值查询
            else {
                ExistsQueryBuilder existsQueryBuilder = QueryBuilders.existsQuery(ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD);
                // 是否反转查询
                this.reversalQuery(reversal, queryBuilder, existsQueryBuilder);
            }
            boolQueryBuilder.must(QueryBuilders.nestedQuery(ESOrderDataDTO.COL_DN, queryBuilder, ScoreMode.None));
        });
    }

    /**
     * 是否反转查询
     *
     * @param reversal         reversal
     * @param boolQueryBuilder boolQueryBuilder
     * @param queryBuilder     queryBuilder
     */
    private void reversalQuery(boolean reversal, BoolQueryBuilder boolQueryBuilder, QueryBuilder queryBuilder) {
        NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery(ESControlDataDTO.COL_DN_VN, queryBuilder, ScoreMode.None);
        if (reversal) {
            // 反转查询
            boolQueryBuilder.mustNot(nestedQueryBuilder);
        } else {
            // 非反转查询
            boolQueryBuilder.must(nestedQueryBuilder);
        }
    }

    private Query handle(RefreshAppOrderReqDTO refreshAppOrderReqDTO) {
        // 创建bool多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (Objects.nonNull(refreshAppOrderReqDTO.getEnterpriseId())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(ESOrderDataDTO.COL_EI, refreshAppOrderReqDTO.getEnterpriseId()));
        }
        if (Objects.nonNull(refreshAppOrderReqDTO.getApplicationId())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(ESOrderDataDTO.COL_AI, refreshAppOrderReqDTO.getApplicationId()));
        }
        if (Objects.nonNull(refreshAppOrderReqDTO.getApplicationKey())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery(ESOrderDataDTO.COL_AK, refreshAppOrderReqDTO.getApplicationKey()));
        }

        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(boolQueryBuilder);
        queryBuilder.withTrackTotalHits(true);
        return queryBuilder.build();
    }

    /**
     * 处理筛选条件
     *
     * @param queryBuilder queryBuilder
     * @param judgeOrs     judgeOrs
     */
    private void handleJudgeOrs(BoolQueryBuilder queryBuilder, List<JudgeOr> judgeOrs, boolean forceUseValue) {
        // 补充控件code
        List<Long> controlIds = new ArrayList<>();
        judgeOrs.forEach(or -> or.getJudgeAnd().forEach(and -> controlIds.add(and.getControlId())));
        Map<Long, String> controlCodeMap = applicationFormControlService.controlCodeMap(controlIds);
        judgeOrs.forEach(or -> or.getJudgeAnd().forEach(and -> and.setControlCode(controlCodeMap.getOrDefault(and.getControlId(), and.getControlCode()))));

        BoolQueryBuilder orsQueryBuilder = QueryBuilders.boolQuery();
        judgeOrs.forEach(or -> {
            BoolQueryBuilder orQueryBuilder = QueryBuilders.boolQuery();
            or.getJudgeAnd().forEach(and -> {
                BoolQueryBuilder andQueryBuilder = QueryBuilders.boolQuery();
                ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(and.getControlCode());
                // 表单控件id
                andQueryBuilder.must(QueryBuilders.termQuery(ESControlDataDTO.COL_DN_CI, and.getControlId()));

                boolean valueUseId = controlCodeEnum.valueUseId();
                boolean isDate = controlCodeEnum.isDate();
                String field = valueUseId && !forceUseValue ? ESControlDataValueDTO.COL_DN_VN_ID :
                        (isDate ? ESControlDataValueDTO.COL_DN_VN_DATE : ESControlDataValueDTO.COL_DN_VN_DV_KEYWORD);
                Function<String, Object> valueFormat = valueUseId && !forceUseValue ? Long::valueOf : value -> value;

                boolean has = false;
                AbstractQueryBuilder<?> valueQueryBuilders;
                if (CollectionUtil.isNotEmpty(and.getValues())) {
                    has = true;
                    valueQueryBuilders = QueryBuilders.termsQuery(field, and.getValues().stream().map(valueFormat).toArray());
                } else {
                    valueQueryBuilders = QueryBuilders.existsQuery(field);
                }
                JudgeSignEnum signEnum = JudgeSignEnum.isExistById(and.getJudgeSign());
                switch (signEnum) {
                    case EQUALS:
                        // 等于 不需要反转
                        this.reversalQuery(!has, andQueryBuilder, valueQueryBuilders);
                        break;
                    case UN_EQUALS:
                        // 不等于 需要反转
                        this.reversalQuery(has, andQueryBuilder, valueQueryBuilders);
                        break;
                    case CONTAIN:
                        // 多个值
                        if (controlCodeEnum.isMultiple()) {
                            if (has) {
                                // 有条件
                                this.reversalQuery(false, andQueryBuilder, QueryBuilders.termsQuery(field, and.getValues().stream().map(valueFormat).toArray()));
                            }
                        }
                        // 单个值
                        else {
                            if (has) {
                                // 有条件
                                String value = and.getValues().stream().findFirst().orElseThrow(() -> new BusinessException(RCodeEnum.ERROR_RUNTIME));
                                this.reversalQuery(false, andQueryBuilder, QueryBuilders.wildcardQuery(field, String.format("*%s*", value)));
                            }
                        }
                        break;
                    case LT:
                    case LTE:
                    case GT:
                    case GTE:
                        // 不支持多个值的比较
                        if (!controlCodeEnum.isMultiple()) {
                            if (has) {
                                // 有条件
                                String value = and.getValues().stream().findFirst().orElseThrow(() -> new BusinessException(RCodeEnum.ERROR_RUNTIME));
                                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(field);
                                if (JudgeSignEnum.LT == signEnum) {
                                    this.handleDate(isDate, value, rangeQueryBuilder::lt);
                                } else if (JudgeSignEnum.LTE == signEnum) {
                                    this.handleDate(isDate, value, rangeQueryBuilder::lte);
                                } else if (JudgeSignEnum.GT == signEnum) {
                                    this.handleDate(isDate, value, rangeQueryBuilder::gt);
                                } else {
                                    this.handleDate(isDate, value, rangeQueryBuilder::gte);
                                }
                                this.reversalQuery(false, andQueryBuilder, rangeQueryBuilder);
                            }
                        }
                        break;
                    default:
                        throw new BusinessException(RCodeEnum.ERROR_RUNTIME);
                }
                orQueryBuilder.must(QueryBuilders.nestedQuery(ESOrderDataDTO.COL_DN, andQueryBuilder, ScoreMode.None));
            });
            orsQueryBuilder.should(orQueryBuilder);
        });
        queryBuilder.must(orsQueryBuilder);
    }

    /**
     * 处理关键字
     *
     * @param keyword    keyword
     * @param fuzzyQuery fuzzyQuery
     */
    private QueryBuilder handleKeyword(@NotBlank String keyword, Integer fuzzyQuery, String field) {
        if (SwitchEnum.isOpen(fuzzyQuery)) {
            // 模糊查询
            return QueryBuilders.wildcardQuery(field, String.format("*%s*", keyword));
        } else {
            // 精准查询
            return QueryBuilders.termQuery(field, keyword);
        }
    }

    private AbstractQueryBuilder<?> handleDate(boolean isDate, String value, Function<String, AbstractQueryBuilder<?>> query) {
        AbstractQueryBuilder<?> queryBuilder;
        if (isDate) {
            try {
                queryBuilder = query.apply(DateUtil.formatDateTime(DateUtil.parseDateTime(value)));
            } catch (Exception e) {
                throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "日期范围查询参数错误");
            }
            if (queryBuilder instanceof RangeQueryBuilder) {
                ((RangeQueryBuilder) queryBuilder)
                        .format(DatePattern.NORM_DATETIME_PATTERN)
                        .timeZone(AppConstant.DEFAULT_TIME_ZONE.toZoneId().getId());
            }
        } else {
            queryBuilder = query.apply(value);
        }
        return queryBuilder;
    }

    /**
     * 处理排序
     *
     * @param queryBuilder queryBuilder
     * @param queryModel   queryModel
     */
    private void handleSort(NativeSearchQueryBuilder queryBuilder, QueryModel<?> queryModel) {
        List<Long> controlIds = queryModel.getOrders().stream().map(e -> {
            try {
                return Long.valueOf(e.getField());
            } catch (NumberFormatException ex) {
                throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "排序参数错误");
            }
        }).collect(Collectors.toList());
        Map<Long, String> controlCodeMap = applicationFormControlService.controlCodeMap(controlIds);

        if (!queryModel.hasOrders()) {
            // 默认更新时间倒序
            queryModel.addOrders(new Order(Long.toString(OrderSpecialControlEnum.UPDATE.getId()), true));
        }
        queryModel.getOrders().stream().distinct().forEach(order -> {
            Long controlId = Long.valueOf(order.getField());
            SortOrder sortOrder = order.isDesc() ? SortOrder.DESC : SortOrder.ASC;
            // 特殊处理-订单编号
            if (OrderSpecialControlEnum.SN.getId().equals(controlId)) {
                queryBuilder.withSort(SortBuilders.fieldSort(ESOrderDataDTO.COL_ID).order(sortOrder));
            }
            // 特殊处理-更新时间
            else if (OrderSpecialControlEnum.UPDATE.getId().equals(controlId)) {
                queryBuilder.withSort(SortBuilders.fieldSort(ESOrderDataDTO.COL_UPDATED).order(sortOrder));
            }
            // 特殊处理-更新时间
            else if (OrderSpecialControlEnum.CREATE.getId().equals(controlId)) {
                queryBuilder.withSort(SortBuilders.fieldSort(ESOrderDataDTO.COL_CREATED).order(sortOrder));
            } else {
                if (!controlCodeMap.containsKey(controlId)) {
                    throw new BusinessException(RCodeEnum.ERROR_PARAMETER, "排序参数错误");
                }
                ControlCodeEnum controlCodeEnum = ControlCodeEnum.findByCode(controlCodeMap.get(controlId));
                String key = ESControlDataValueDTO.COL_DN_V_DV;
                // 日期排序
                if (controlCodeEnum.isDate()) {
                    key = ESControlDataValueDTO.COL_DN_V_DATE;
                }
                // 数字排序
                else if (ControlCodeEnum.INPUT_NUMBER == controlCodeEnum) {
                    key = ESControlDataValueDTO.COL_DN_V_DATA;
                }
                queryBuilder.withSort(SortBuilders.fieldSort(key)
                        .setNestedSort(new NestedSortBuilder(ESOrderDataDTO.COL_DN)
                                .setFilter(QueryBuilders.termQuery(ESControlDataDTO.COL_DN_CI, controlId)))
                        .order(sortOrder));
            }
        });
    }

    private void handleRow(AggregationDTO aggregationDTO, List<ReportingHeaderDTO> reportingHeaders) {
        for (ReportingHeaderDTO header : reportingHeaders) {
            aggregationDTO.beingTableControl = header.getBeingTableControl();
            aggregationDTO.needRev = !aggregationDTO.preBeingTableControl || !aggregationDTO.beingTableControl;
            this.handleAggregation(aggregationDTO, false, header);
        }
    }

    private void handleCell(AggregationDTO aggregationDTO, List<ReportingHeaderDTO> reportingHeaders) {
        for (ReportingHeaderDTO header : reportingHeaders) {
            aggregationDTO.beingTableControl = header.getBeingTableControl();
            aggregationDTO.needRev = !aggregationDTO.preBeingTableControl || !aggregationDTO.beingTableControl;
            this.handleAggregation(aggregationDTO, false, header);
        }
    }

    private void handleTarget(AggregationDTO aggregationDTO, List<ReportingTargetDTO> targets) {
        String title = aggregationDTO.getTitle();
        boolean hasRoot = aggregationDTO.hasRoot();
        AggregationDTO current = hasRoot ? new AggregationDTO(aggregationDTO) : aggregationDTO;
        current.needRev = !current.preBeingTableControl || !current.beingTableControl;
        for (ReportingTargetDTO target : targets) {
            current.title = title;
            // 数据总量 统计订单号数量
            if (ReportingTargetTypeEnum.DATA_SUMMARY.getId() == target.getTargetType()) {
                target.setControlId(OrderSpecialControlEnum.SN.getId());
                target.setSummaryType(SummaryTypeEnum.COUNT.getId());
            }
            current.beingTableControl = target.getBeingTableControl();
            this.handleAggregation(current, !hasRoot, target);
            title = current.title;
            current.needRev = false;
        }
    }

    private void handleAggregation(AggregationDTO aggregationDTO, boolean onlyTarget, AggBaseDTO aggBaseDTO) {
        String title = aggBaseDTO instanceof ReportingTargetDTO ? ((ReportingTargetDTO) aggBaseDTO).getKey() : aggBaseDTO.getTitle();
        // 是否需要反转
        if (Objects.nonNull(aggregationDTO.last) && aggregationDTO.needRev) {
            ReverseNestedAggregationBuilder rev = AggregationBuilders.reverseNested(ElasticsearchUtil.rev(aggregationDTO.title));
            aggregationDTO.last.subAggregation(rev);
            aggregationDTO.last = rev;
        }
        aggregationDTO.title = title;

        String controlField;
        String nestedField;
        String valueField;
        // 上层为表格层（基于 table_values_nested.row 去汇总）
        if (aggregationDTO.preBeingTableControl && aggregationDTO.beingTableControl) {
            // control 层
            controlField = ESControlDataDTO.COL_DN_TVN_R_CI;
            nestedField = ESControlDataDTO.COL_DN_TVN_R;
            valueField = ESControlDataValueDTO.COL_DN_TVN_R_V_DV_KEYWORD;
        }
        // 当前为表格层（基于 table_values 去汇总）
        else if (aggregationDTO.beingTableControl) {
            // control 层
            controlField = ESControlDataDTO.COL_DN_TVN_CI;
            nestedField = ESControlDataDTO.COL_DN_TVN;
            valueField = ESControlDataValueDTO.COL_DN_TVN_V_DV_KEYWORD;
        }
        // 当前为普通层（基于 data_nested 去汇总）
        else {
            // control 层
            controlField = ESControlDataDTO.COL_DN_CI;
            nestedField = ESOrderDataDTO.COL_DN;
            valueField = ESControlDataValueDTO.COL_DN_V_DV_KEYWORD;
        }

        // value 层
        String value = ElasticsearchUtil.value(title);
        ValuesSourceAggregationBuilder<?> valueAgg;
        if (aggBaseDTO instanceof ReportingTargetDTO) {
            // 有汇总方式：结束了，则运算处理
            valueAgg = this.summaryAggregation(value, ((ReportingTargetDTO) aggBaseDTO).getSummaryType(), valueField);
        } else {
            // 没有汇总方式：还没结束，则分桶处理
            ReportingHeaderDTO header = (ReportingHeaderDTO) aggBaseDTO;
            // 时间格式化
            if (null != header.getDateType()) {
                valueAgg = this.aggDate(value, valueField, header.getDateType());
            } else {
                valueAgg = AggregationBuilders.terms(value).field(valueField).missing("").size(20000);
            }
        }
        // control 层
        FilterAggregationBuilder tableControlAgg = AggregationBuilders
                .filter(ElasticsearchUtil.control(title), QueryBuilders
                        .termQuery(controlField, aggBaseDTO.getControlId()))
                .subAggregation(valueAgg);
        // nested 层
        NestedAggregationBuilder nested = AggregationBuilders
                .nested(ElasticsearchUtil.nested(title), nestedField)
                .subAggregation(tableControlAgg);

        if (aggregationDTO.hasLast()) aggregationDTO.last.subAggregation(nested);
        if (!aggregationDTO.hasRoot()) aggregationDTO.root = nested;
        if (aggBaseDTO instanceof ReportingHeaderDTO) aggregationDTO.last = valueAgg;
        if (onlyTarget) aggregationDTO.targets.add(nested);
        aggregationDTO.preBeingTableControl = aggregationDTO.beingTableControl;
    }

    private ValuesSourceAggregationBuilder<?> summaryAggregation(String title, int summaryType, String valueField) {
        ValuesSourceAggregationBuilder<?> value;
        SummaryTypeEnum summaryTypeEnum = SummaryTypeEnum.isExistById(summaryType);
        switch (summaryTypeEnum) {
            case MAX:
                value = AggregationBuilders.max(title);
                break;
            case MIN:
                value = AggregationBuilders.min(title);
                break;
            case SUM:
                value = AggregationBuilders.sum(title);
                break;
            case COUNT:
                value = AggregationBuilders.count(title);
                break;
            case AVERAGE:
                value = AggregationBuilders.avg(title);
                break;
            default:
                value = AggregationBuilders.missing(title);
                break;
        }
        if (SummaryTypeEnum.COUNT == summaryTypeEnum) {
            value.field(valueField);
        } else {
            String scriptFormat = "try{ if(doc['%s'].size()>0 ){ return Double.parseDouble(doc['%s'].value);} else{ return 0} } catch(Exception e) { return 0;}";
            String script = String.format(scriptFormat, valueField, valueField);
            value.script(new Script(script)).format(".0000000000");
        }
        return value;
    }

    private ValuesSourceAggregationBuilder<?> aggDate(String title, String valueField, Integer dateType) {
        DateTypeEnum dateTypeEnum = DateTypeEnum.isExistById(dateType);
        DateHistogramInterval interval;
        switch (dateTypeEnum) {
            case DATE:
                interval = DateHistogramInterval.DAY;
                break;
            case MONTH:
                interval = DateHistogramInterval.DAY;
                break;
            case YEAR:
                interval = DateHistogramInterval.DAY;
                break;
            default:
                throw DateTypeEnum.getBusinessException();
        }

        // 格式化需要去把 *.data_value.keyword 替换成 *.date
        String front = String.format("%s.%s", ESControlDataValueDTO.COL_DV, ESFieldUtil.KEYWORD);
        valueField = valueField.replace(front, ESControlDataValueDTO.COL_DATE);
        return AggregationBuilders.dateHistogram(title).field(valueField).fixedInterval(interval).format(dateTypeEnum.getPattern());
    }

    @Data
    private static class AggregationDTO {
        private AbstractAggregationBuilder<?> root;
        private List<AbstractAggregationBuilder<?>> targets;
        private AbstractAggregationBuilder<?> last;

        private String title;
        private Boolean preBeingTableControl;
        private Boolean beingTableControl;
        private Boolean needRev;

        public AggregationDTO(AggregationDTO aggregationDTO) {
            this.root = aggregationDTO.root;
            this.targets = aggregationDTO.targets;
            this.last = aggregationDTO.last;

            this.title = aggregationDTO.title;
            this.preBeingTableControl = aggregationDTO.preBeingTableControl;
            this.beingTableControl = aggregationDTO.beingTableControl;
            this.needRev = aggregationDTO.needRev;
        }

        public AggregationDTO(AbstractAggregationBuilder<?> root, AbstractAggregationBuilder<?> last) {
            this.root = root;
            this.targets = new ArrayList<>();
            this.last = last;

            this.title = "";
            this.preBeingTableControl = false;
            this.beingTableControl = false;
            this.needRev = false;
        }

        public boolean hasRoot() {
            return Objects.nonNull(this.root);
        }

        public boolean hasLast() {
            return Objects.nonNull(this.last);
        }
    }

    // region Summary
    private static class RelatedSummary extends Summary<String> {

        public RelatedSummary(String title, String value) {
            super(title, value);
        }

        @Override
        public String getTitle() {
            return this.getT();
        }

        @Override
        public Summary<String> copy() {
            return new RelatedSummary(this.getT(), this.getValue());
        }

        @Override
        public Summary<String> copy(String value) {
            return new RelatedSummary(this.getT(), value);
        }
    }
    // endregion
}
